package com.yangchuang.openpicture.service.impl;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.DeleteObjectsRequest;
import com.aliyun.oss.model.ObjectMetadata;
import com.yangchuang.openpicture.communication.ResultCode;
import com.yangchuang.openpicture.constant.FileUploadConstants;
import com.yangchuang.openpicture.exception.BussinessException;
import com.yangchuang.openpicture.property.OSSProperties;
import com.yangchuang.openpicture.service.OSSService;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 阿里云OSS对象存储服务实现类 (适配公共读Bucket模式)
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OSSServiceImpl implements OSSService {

    private final OSSProperties ossProperties;
    private OSS ossClient;

    /**
     * 初始化OSS客户端
     */
    @PostConstruct
    public void init() {
        log.info("正在初始化阿里云OSS客户端...");
        try {
            ossClient = new OSSClientBuilder().build(
                    ossProperties.getEndpoint(),
                    ossProperties.getAccessKeyId(),
                    ossProperties.getAccessKeySecret()
            );
            log.info("阿里云OSS客户端初始化成功！Bucket: {}", ossProperties.getBucketName());
        } catch (Exception e) {
            log.error("阿里云OSS客户端初始化失败！请检查Endpoint和AccessKey配置。", e);
            // 初始化失败时，后续调用会因为 ossClient 为 null 而快速失败
        }
    }

    /**
     * 销毁并关闭OSS客户端
     */
    @PreDestroy
    public void destroy() {
        if (ossClient != null) {
            ossClient.shutdown();
            log.info("阿里云OSS客户端已成功关闭。");
        }
    }

    // ========================= 文件上传 (逻辑不变) =========================

    @Override
    public String uploadFile(MultipartFile file, FileUploadConstants fileType) {
        if (file == null || file.isEmpty()) {
            throw new BussinessException(ResultCode.PARAM_INVALID.getCode(), "上传文件不能为空");
        }
        return performUpload(file, fileType);
    }

    @Override
    public List<String> uploadFiles(List<MultipartFile> files, FileUploadConstants fileType) {
        if (CollectionUtils.isEmpty(files)) {
            return Collections.emptyList();
        }
        return files.parallelStream()
                .map(file -> performUpload(file, fileType))
                .collect(Collectors.toList());
    }

    private String performUpload(MultipartFile file, FileUploadConstants fileType) {
        String path = ossProperties.getUploadPaths().get(fileType.getPathType());
        if (!StringUtils.hasText(path)) {
            log.error("未找到文件类型 '{}' 对应的上传路径配置。", fileType.getPathType());
            throw new BussinessException(ResultCode.INTERNAL_ERROR.getCode(), "服务器上传路径配置错误");
        }

        String originalFilename = file.getOriginalFilename();
        if (!StringUtils.hasText(originalFilename)) {
            throw new BussinessException(ResultCode.EMPTY_FILENAME);
        }

        String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
        String uniqueFileName = UUID.randomUUID().toString().replace("-", "") + fileExtension;
        String objectName = path + uniqueFileName;

        try (InputStream inputStream = file.getInputStream()) {
            ossClient.putObject(ossProperties.getBucketName(), objectName, inputStream);
            log.info("文件上传成功: {}", objectName);
        } catch (Exception e) {
            log.error("文件上传到OSS时发生错误: {}", originalFilename, e);
            throw new BussinessException(ResultCode.FILE_UPLOAD_FAILED);
        }
        return objectName;
    }

    // ========================= 文件删除 (逻辑不变) =========================

    @Override
    public void deleteFileByObjectName(String objectName) {
        if (!StringUtils.hasText(objectName)) {
            log.warn("尝试删除的文件ObjectName为空，操作被跳过。");
            return;
        }
        try {
            ossClient.deleteObject(ossProperties.getBucketName(), objectName);
            log.info("文件删除成功: {}", objectName);
        } catch (Exception e) {
            log.error("从OSS删除文件 '{}' 失败。", objectName, e);
            throw new BussinessException(ResultCode.FILE_DELETE_FAILED);
        }
    }

    @Override
    public void deleteFileByUrl(String fileUrl) {
        if (!StringUtils.hasText(fileUrl)) {
            log.warn("尝试根据URL删除文件，但URL为空，操作被跳过。");
            return;
        }
        String objectName = parseObjectNameFromUrl(fileUrl);
        deleteFileByObjectName(objectName);
    }

    @Override
    public void deleteFilesByObjectNames(List<String> objectNames) {
        if (CollectionUtils.isEmpty(objectNames)) {
            log.warn("尝试批量删除文件，但ObjectName列表为空，操作被跳过。");
            return;
        }
        try {
            DeleteObjectsRequest request = new DeleteObjectsRequest(ossProperties.getBucketName()).withKeys(objectNames);
            ossClient.deleteObjects(request);
            log.info("批量删除文件成功，数量: {}", objectNames.size());
        } catch (Exception e) {
            log.error("从OSS批量删除文件失败。", e);
            throw new BussinessException(ResultCode.FILE_DELETE_FAILED);
        }
    }

    // ========================= 公开链接获取 (核心重写逻辑) =========================

    @Override
    public String getSignedUrlByObjectName(String objectName) {
        if (!StringUtils.hasText(objectName)) {
            log.warn("请求获取文件链接，但ObjectName为空。");
            return null;
        }
        // 在公共读模式下，直接返回永久公开URL。expirationInMinutes参数被忽略。
        return this.getFullUrlByObjectName(objectName);
    }

    @Override
    public String getSignedUrlByUrl(String fileUrl, long expirationInMinutes) {
        if (!StringUtils.hasText(fileUrl)) {
            log.warn("请求通过URL获取文件链接，但URL为空。");
            return null;
        }
        // 解析出ObjectName，然后生成标准的公开URL
        String objectName = parseObjectNameFromUrl(fileUrl);
        return this.getFullUrlByObjectName(objectName);
    }

    // ========================= 实用工具方法 (逻辑不变) =========================

    @Override
    public String getFullUrlByObjectName(String objectName) {
        if (!StringUtils.hasText(objectName)) {
            return null;
        }
        // 拼接成 "https://bucket-name.endpoint/object-name" 格式
        return ossProperties.getBaseUrl() + "/" + objectName;
    }

    @Override
    public ObjectMetadata getObjectMetadata(String objectName) {
        if (!StringUtils.hasText(objectName)) {
            throw new BussinessException(ResultCode.PARAM_INVALID.getCode(), "Object Name不能为空");
        }
        try {
            return ossClient.getObjectMetadata(ossProperties.getBucketName(), objectName);
        } catch (Exception e) {
            log.error("获取文件 '{}' 的元数据失败。", objectName, e);
            // 根据业务需求，可以选择返回null或继续抛出异常
            throw new BussinessException(ResultCode.INTERNAL_ERROR.getCode(), "获取文件元数据失败");
        }
    }

    /**
     * 辅助方法：从完整URL中安全地解析出Object Name。
     * 例如："https://naiyi.oss-cn-beijing.aliyuncs.com/images/uuid.jpg?query=1" -> "images/uuid.jpg"
     * @param fileUrl 完整文件URL
     * @return 解析后的Object Name
     */
    private String parseObjectNameFromUrl(String fileUrl) {
        if (!StringUtils.hasText(fileUrl)) {
            throw new BussinessException(ResultCode.PARAM_INVALID.getCode(), "文件URL不能为空");
        }

        String baseUrl = ossProperties.getBaseUrl();
        // 移除URL可能携带的查询参数
        int queryIndex = fileUrl.indexOf('?');
        String mainUrl = (queryIndex != -1) ? fileUrl.substring(0, queryIndex) : fileUrl;

        if (!mainUrl.startsWith(baseUrl + "/")) {
            log.error("URL '{}' 与配置的基础域名 '{}' 不匹配。", fileUrl, baseUrl);
            throw new BussinessException(ResultCode.PARAM_INVALID.getCode(), "非法的资源URL");
        }
        // 截取基础域名和斜杠之后的部分
        return mainUrl.substring(baseUrl.length() + 1);
    }
}